#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the version 3 of the GNU Lesser General Public License
#   as published by the Free Software Foundation.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Copyright (c) NEC Deutschland GmbH, NEC HPC Europe
#
# $Id$

import os.path
from copy import copy

from consumer import Consumer
from basic_types.marshaller.fs_marshaller import FSMarshaller
from basic_types.marshaller.fs_unmarshaller import FSUnmarshaller
from interfaces.physical_topology import *


__all__ = ["PhysicalTopologyDatabase"]


class PhysicalTopologyDatabase(Consumer, PhysicalTopologyInterface):
    ## Adds the directory "clusters" to the directory path.
    # @param base_dir_path: directory path
    # @return: nothing
    def __init__( self, base_dir_path, *args, **kwds ):
        Consumer.__init__( self, *args, **kwds )
        self.__clusters_dir_path = os.path.join( base_dir_path, "clusters" )
        try: os.makedirs( self.__clusters_dir_path )
        except: pass                    
        
    def addCluster( self, cluster ):        
        print "PhysicalTopologyDatabase: adding cluster", cluster.name
        
        cluster_name = cluster.name
        cluster = copy( cluster )
        cluster.name = ""
        FSMarshaller( self.__clusters_dir_path ).writeStruct( cluster_name, cluster )        
        
    def addRack( self, cluster_name, rack ):
        print "PhysicalTopologyDatabase: adding rack", rack.name, "to cluster", cluster_name
                    
        rack_name = rack.name
        rack = copy( rack )
        rack.name = ""
        FSMarshaller( os.path.join( self.__clusters_dir_path, cluster_name, "racks" ) ).writeStruct( rack_name, rack )
    
    def addHost( self, cluster_name, rack_name, host ):
        print "PhysicalTopologyDatabase: adding host", host.name, "to rack", rack_name
        
        host_name = host.name
        host = copy( host )
        host.name = ""
        FSMarshaller( os.path.join( self.__clusters_dir_path, cluster_name, "racks", rack_name, "hosts" ) ).writeStruct( host_name, host )
    
    def getClusterByHostName( self, host_name ):
        for cluster_name in os.listdir( self.__clusters_dir_path ):
            racks_dir_path = os.path.join( self.__clusters_dir_path, cluster_name, "racks" )
            if os.path.exists( racks_dir_path ):
                for rack_name in os.listdir( racks_dir_path ):
                    hosts_dir_path = os.path.join( racks_dir_path, "hosts" )
                    if os.path.exists( hosts_dir_path ):
                        for check_host_name in os.listdir( hosts_dir_path ):
                            if check_host_name == host_name or check_host_name.startswith( host_name + "." ):
                                return FSUnmarshaller( self.__clusters_dir_path, depth=1 ).readStruct( cluster_name, Cluster() )
        
        raise KeyError, "no such host: " + host_name 

    def getClusters( self ):
        clusters = ClusterMap()
        for cluster_name in os.listdir( self.__clusters_dir_path ):
            clusters[cluster_name] = FSUnmarshaller( self.__clusters_dir_path, depth=1 ).readStruct( cluster_name, Cluster() )
        return clusters

    def getHostByName( self, host_name ):
        return FSUnmarshaller( os.path.join( self._getHostDirPathByHostName( host_name ), ".." ), depth=1 ).readStruct( host_name, Host() )
  
    def _getHostDirPathByHostName( self, host_name ):                                 
        for cluster_name in os.listdir( self.__clusters_dir_path ):
            racks_dir_path = os.path.join( self.__clusters_dir_path, cluster_name, "racks" )
            if os.path.exists( racks_dir_path ):
                for rack_name in os.listdir( racks_dir_path ):
                    hosts_dir_path = os.path.join( racks_dir_path, rack_name, "hosts" )
                    if os.path.exists( hosts_dir_path ):
                        for check_host_name in os.listdir( hosts_dir_path ):
                            if check_host_name == host_name or check_host_name.startswith( host_name + "." ):                                        
                                return os.path.join( hosts_dir_path, check_host_name )              

        raise KeyError, "no such host_name '" + host_name + "'"

    def getHostsByRackName( self, rack_name ):
        hosts = HostMap()
        hosts_dir_path = os.path.join( self._getRackDirPathByRackName( rack_name ), "hosts" )        
        for host_name in os.listdir( hosts_dir_path ):
            hosts[host_name] = FSUnmarshaller( hosts_dir_path, depth=1 ).readStruct( host_name, Host() )
        return hosts
       
    def _getRackDirPathByRackName( self, rack_name ):
        for cluster_name in os.listdir( self.__clusters_dir_path ):
            racks_dir_path = os.path.join( self.__clusters_dir_path, cluster_name, "racks" )
            if os.path.exists( os.path.join( racks_dir_path, rack_name ) ):
                return os.path.join( racks_dir_path, rack_name )
            
        raise KeyError, "no such rack: " + rack_name 

    def getRacksByClusterName( self, cluster_name ):        
        for check_cluster_name in os.listdir( self.__clusters_dir_path ):
            if check_cluster_name == cluster_name:
                racks = RackMap()
                racks_dir_path = os.path.join( self.__clusters_dir_path, cluster_name, "racks" )
                if os.path.exists( racks_dir_path ):
                    for rack_name in os.listdir( racks_dir_path ):
                        racks[rack_name] = FSUnmarshaller( racks_dir_path, depth=1 ).readStruct( rack_name, Rack() )
                return racks
            
        raise KeyError, "no such cluster: " + cluster_name
